package org.javahispano.javacup.tacticas_aceptadas.osorio;

import java.awt.Color;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import org.javahispano.javacup.modelo.Comando;
import org.javahispano.javacup.modelo.ComandoGolpearBalon;
import org.javahispano.javacup.modelo.ComandoIrA;
import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.EstiloUniforme;
import org.javahispano.javacup.modelo.JugadorDetalle;
import org.javahispano.javacup.modelo.Posicion;
import org.javahispano.javacup.modelo.SituacionPartido;
import org.javahispano.javacup.modelo.Tactica;
import org.javahispano.javacup.modelo.TacticaDetalle;

public class Masia implements Tactica {

    Posicion alineacion1[]=new Posicion[]{
        new Posicion(0.2595419847328244,-50.41044776119403),
        new Posicion(-11.65034965034965,-27.31900452488688),
        new Posicion(-25.916083916083913,-18.529411764705884),
        new Posicion(28.531468531468533,-17.57918552036199),
        new Posicion(11.412587412587413,-28.50678733031674),
        new Posicion(1.188811188811189,-15.441176470588236),
        new Posicion(-18.545454545454543,-5.701357466063349),
        new Posicion(17.594405594405593,-6.176470588235294),
        new Posicion(0.23776223776223776,-0.9502262443438915),
        new Posicion(3.5664335664335667,-1.4253393665158371),
        new Posicion(-8.797202797202797,-0.47511312217194573)
    };

    Posicion alineacion2[]=new Posicion[]{
        new Posicion(0.2595419847328244,-50.41044776119403),
        new Posicion(-11.888111888111888,-30.16968325791855),
        new Posicion(-26.153846153846157,-21.855203619909503),
        new Posicion(27.104895104895103,-21.380090497737555),
        new Posicion(12.125874125874127,-29.93212669683258),
        new Posicion(-0.23776223776223776,-17.34162895927602),
        new Posicion(-16.643356643356643,-10.927601809954751),
        new Posicion(13.552447552447552,-9.264705882352942),
        new Posicion(10.461538461538462,-0.47511312217194573),
        new Posicion(26.153846153846157,-1.6628959276018098),
        new Posicion(-23.776223776223777,-1.1877828054298643)
    };

    Posicion alineacionDesmarque[]=new Posicion[]{
    		new Posicion(0.2595419847328244,-50.41044776119403),
            new Posicion(-9.986013986013985,0.23755656108597287),
            new Posicion(-24.965034965034967,14.96606334841629),
            new Posicion(25.916083916083913,15.91628959276018),
            new Posicion(8.797202797202797,0.47511312217194573),
            new Posicion(-0.7132867132867133,26.131221719457013),
            new Posicion(-18.545454545454543,34.68325791855204),
            new Posicion(17.356643356643357,34.44570135746606),
            new Posicion(-0.7132867132867133,41.0972850678733),
            new Posicion(10.223776223776223,46.3235294117647),
            new Posicion(-9.510489510489512,46.79864253393666)
    };

    class TacticaDetalleImpl implements TacticaDetalle {

        public String getNombre() {
            return "Masia";
        }

        public String getPais() {
            return "Cuba";
        }

        public String getEntrenador() {
            return "osorio";
        }

        public Color getColorCamiseta() {
            return new Color(255, 255, 0);
        }

        public Color getColorPantalon() {
            return new Color(0, 0, 255);
        }

        public Color getColorFranja() {
            return new Color(153, 0, 51);
        }

        public Color getColorCalcetas() {
            return new Color(0, 0, 255);
        }

        public Color getColorPortero() {
            return new Color(0, 255, 51        );
        }

        public EstiloUniforme getEstilo() {
            return EstiloUniforme.SIN_ESTILO;
        }

        public Color getColorCamiseta2() {
            return new Color(0, 255, 51);
        }

        public Color getColorPantalon2() {
            return new Color(0, 0, 0);
        }

        public Color getColorFranja2() {
            return new Color(204, 0, 0);
        }

        public Color getColorCalcetas2() {
            return new Color(255, 255, 0);
        }

        public Color getColorPortero2() {
            return new Color(51, 51, 51        );
        }

        public EstiloUniforme getEstilo2() {
            return EstiloUniforme.SIN_ESTILO;
        }

        class JugadorImpl implements JugadorDetalle {

            String nombre;
            int numero;
            Color piel, pelo;
            double velocidad, remate, presicion;
            boolean portero;
            Posicion posicion;

            public JugadorImpl(String nombre, int numero, Color piel, Color pelo,
                    double velocidad, double remate, double presicion, boolean portero) {
                this.nombre=nombre;
                this.numero=numero;
                this.piel=piel;
                this.pelo=pelo;
                this.velocidad=velocidad;
                this.remate=remate;
                this.presicion=presicion;
                this.portero=portero;
            }

            public String getNombre() {
                return nombre;
            }

            public Color getColorPiel() {
                return piel;
            }

            public Color getColorPelo() {
                return pelo;
            }

            public int getNumero() {
                return numero;
            }

            public boolean esPortero() {
                return portero;
            }

            public double getVelocidad() {
                return velocidad;
            }

            public double getRemate() {
                return remate;
            }

            public double getPresicion() {
                return presicion;
            }

        }

        public JugadorDetalle[] getJugadores() {
            return new JugadorDetalle[]{
            		new JugadorImpl("VALD�S", 1, new Color(255,200,150), new Color(50,0,0),1.0d,1.0d,0.0d, true),
                    new JugadorImpl("M�RQUEZ", 4, new Color(255,200,150), new Color(50,0,0),1.0d,0.5d,1.0d, false),
                    new JugadorImpl("PUYOL", 5, new Color(255,200,150), new Color(50,0,0),1.0d,0.5d,0.9d, false),
                    new JugadorImpl("ALVES", 20, new Color(133,65,3), new Color(50,0,0),1.0d,0.5d,0.9d, false),
                    new JugadorImpl("PIQU�", 3, new Color(255,200,150), new Color(50,0,0),1.0d,0.5d,1.0d, false),
                    new JugadorImpl("TOUR�", 24, new Color(47,23,1), new Color(0,0,0),0.6d,0.7d,1.0d, false),
                    new JugadorImpl("INIESTA", 8, new Color(255,200,150), new Color(50,0,0),0.72d,0.7d,1.0d, false),
                    new JugadorImpl("XAVI", 6, new Color(255,200,150), new Color(50,0,0),0.73d,0.73d,1.0d, false),
                    new JugadorImpl("ETOO", 9, new Color(46,23,2), new Color(0,0,0),1.0d,1.0d,1.0d, false),
                    new JugadorImpl("MESSI", 10, new Color(255,200,150), new Color(50,0,0),0.95d,1.0d,0.81d, false),
                    new JugadorImpl("HENRY", 14, new Color(111,65,24), new Color(50,0,0),1.0d,0.95d,0.81d, false)
            };
        }
    }

    TacticaDetalle detalle=new TacticaDetalleImpl();
    public TacticaDetalle getDetalle() {
        return detalle;
    }

    public Posicion[] getPosicionSaca(SituacionPartido sp) {
    return alineacion1;
    }

    public Posicion[] getPosicionRecive(SituacionPartido sp) {
    return alineacion2;
    }

    List<Posicion> posiciones = new ArrayList<Posicion>();
    List<Double> marca = new ArrayList<Double>();
    
    public Masia() {
    	int cantFilas = 15;
    	double dy = Math.abs( Constantes.centroArcoSup.getY() - Constantes.centroArcoInf.getY())/(cantFilas+1);
    	int cantColumnas = 10;
    	double dx = Math.abs( Constantes.cornerSupDer.getX() - Constantes.cornerSupIzq.getX())/(cantColumnas+1);
    	for(int i = 0; i < cantColumnas; i++){
    		for(int j = 0; j < cantFilas; j++){
    			posiciones.add(new Posicion(Constantes.cornerSupIzq.getX() + dx*(i+1), Constantes.cornerSupIzq.getY() - dy*(j+1)));
    			marca.add((double)0);
    		}
    	}
	}

    boolean defiende(int jugador){
    	return jugador >=1 && jugador <= 7; 
    }
    
    boolean ataca(int jugador){
    	return jugador >= 5; 
    }
    
	LinkedList<Comando> comandos = new LinkedList<Comando>();
	boolean atacando = false;
	double distanciaMinima = 5;
	double distanciaPresion = 3;
	double distanciaMinimaPase = 5;
	double presionMaximaY = (Constantes.penalSup.getY() + Constantes.centroCampoJuego.getY())/2;
	int posPortero = 0;
	int posPorteroRival = -1;
	double distanciaMaximaDisparo = 30;
	double distanciaMinimaPaseAlto = 20;
	double distanciaDisparo = 15;
	Random rand = new Random();
	
    public List<Comando> ejecutar(SituacionPartido sp) {
        comandos.clear();
        if(sp.saco()){
        	sacar(sp);
        	return comandos;
        }
        
        if( sp.puedenRematarRival().length > 0){
        	atacando = false;
        }else if ( sp.puedenRematar().length > 0){
        	atacando = true;
        }
        
        calcularMarca(sp);
        if(atacando){
	        desmarcarse(sp);
	        irAlBalon(sp);
	        atacar(sp, false);
        }
        else{
        	presionar(sp);
        	recuperarBalon(sp);
        	atacar(sp, true);
        }
        
        portero(sp);
        return comandos;   
    }

	private void sacar(SituacionPartido sp) {
		atacando = true;
    	int[] rematan = sp.puedenRematar();
    	int masCercano = sp.balon().indiceMasCercano(sp.misJugadores());
    	comandos.add(new ComandoIrA(masCercano, sp.balon()));
        for(int i: rematan){
        	comandos.add(new ComandoGolpearBalon(i, Constantes.penalSup, 1, true));
        }
	}

	private void irAlBalon(SituacionPartido sp) {
		int masCercaBalon = sp.balon().indiceMasCercano(sp.misJugadores());
        comandos.add(new ComandoIrA(masCercaBalon, sp.balon()));
        double distanciabalon = sp.misJugadores()[masCercaBalon].distancia(sp.balon()); 
        if(distanciabalon > Constantes.DISTANCIA_CONTROL_BALON){
	        int[] masCercanosBalon = sp.balon().indicesMasCercanos(sp.misJugadores(), masCercaBalon);
	        for (int i = 0; i < masCercanosBalon.length; i++) {
	        	if(sp.misJugadores()[masCercaBalon].distancia(sp.misJugadores()[masCercanosBalon[i]]) > distanciabalon){
	        		comandos.add(new ComandoIrA(masCercanosBalon[i], sp.balon()));
	        		break;
	        	}
			}
        }
	}

	private void desmarcarse(SituacionPartido sp) {
		for (int i = 0; i < 11; i++) {
            if(ataca(i))
            	comandos.add(new ComandoIrA(i, desmarque(i, sp)));
        }
	}

	private void avanzar(int jugador, SituacionPartido sp) {
		comandos.add(new ComandoIrA(jugador, Constantes.penalSup));
        comandos.add(new ComandoGolpearBalon(jugador));
	}

	private void atacar(SituacionPartido sp, boolean pase) {
		int rematan[] = sp.puedenRematar();
        for (int jugador : rematan) {
        	double mejor = 0;
    		int mejorPase = -1; 
        	for (int i = 0; i < 11; i++) {
        		double dist = sp.misJugadores()[jugador].distancia(sp.misJugadores()[i]);
        		if(i != posPortero && dist >= distanciaMinimaPase && posicionDisparo(sp, sp.misJugadores()[i]) > posicionDisparo(sp, sp.misJugadores()[jugador])){
        			double d = sp.rivales()[sp.misJugadores()[i].indiceMasCercano(sp.rivales())].distancia(sp.misJugadores()[i]) / dist;
        			if(d > mejor){
        				mejor = d;
        				mejorPase = i;
        			}
        		}
        	}
        	if(mejorPase > -1){
        		double distancia = sp.misJugadores()[mejorPase].distancia(sp.misJugadores()[jugador]); 
        		boolean alto = distancia > distanciaMinimaPaseAlto;
        		comandos.add(new ComandoGolpearBalon(jugador, sp.misJugadores()[mejorPase],fuerzaPase(sp, jugador, distancia), alto));
            }
        	else{
        		if(pase)
        			pasar(sp);
        		else
        			avanzar(jugador,sp);
        		disparar(jugador, sp);
        	}
        }	
	}
	
	private double posicionDisparo(SituacionPartido sp, Posicion posicion) {
		return distanciaDisparo/Math.max(posicion.distancia(Constantes.centroArcoSup), distanciaDisparo);
	}

	private void disparar(int jugador, SituacionPartido sp) {
		double distancia = sp.misJugadores()[jugador].distancia(Constantes.centroArcoSup);
		if( distancia < distanciaMaximaDisparo && (distancia <= distanciaDisparo || rand.nextDouble()*distanciaMaximaDisparo > distancia)){
    		Posicion disparo = Constantes.posteDerArcoSup;
    		if(porteroRival(sp).distancia(Constantes.posteDerArcoSup)/sp.misJugadores()[jugador].distancia(Constantes.posteDerArcoSup)  < porteroRival(sp).distancia(Constantes.posteIzqArcoSup)/sp.misJugadores()[jugador].distancia(Constantes.posteIzqArcoSup))
    			disparo = Constantes.posteIzqArcoSup;
    		disparo = Posicion.media(disparo, porteroRival(sp));
    		comandos.add(new ComandoGolpearBalon(jugador,disparo , 1, Constantes.ANGULO_VERTICAL*sp.misJugadores()[jugador].distancia(Constantes.centroArcoSup) / distanciaMaximaDisparo));
    	}
	}
	
	
	private Posicion porteroRival(SituacionPartido sp) {
		if(posPorteroRival == -1){
			for (int i = 0; i < 11; i++) { 
				if(sp.detalleJugadoresRivales()[i].esPortero()){
					posPorteroRival = i;
					break;
				}
			}
    	}
		return sp.rivales()[posPorteroRival];
	}

	private void pasar(SituacionPartido sp) {
		int rematan[] = sp.puedenRematar();
        for (int jugador : rematan) {
        	double mejor = 0;
    		int mejorPase = -1; 
        	for (int i = 0; i < 11; i++) {
        		double dist = sp.misJugadores()[jugador].distancia(sp.misJugadores()[i]);
        		if(i != posPortero && dist >= distanciaMinimaPase){
        			double d = sp.rivales()[sp.misJugadores()[i].indiceMasCercano(sp.rivales())].distancia(sp.misJugadores()[i]) / dist;
        			if(d > mejor){
        				mejor = d;
        				mejorPase = i;
        			}
        		}
        	}
        	double distancia = sp.misJugadores()[mejorPase].distancia(sp.misJugadores()[jugador]); 
        	boolean alto = distancia > distanciaMinimaPaseAlto;
            comandos.add(new ComandoGolpearBalon(jugador, sp.misJugadores()[mejorPase],fuerzaPase(sp, jugador, distancia), alto));
        }	
	}

	private double fuerzaPase(SituacionPartido sp, int jugador, double distancia) {
		return (distancia/distanciaMaximaDisparo)/sp.detalleMisJugadores()[jugador].getRemate();
	}

	private void recuperarBalon(SituacionPartido sp) {
		int masCercaBalon = sp.balon().indiceMasCercano(sp.misJugadores());
        comandos.add(new ComandoIrA(masCercaBalon, sp.balon()));
        double distanciabalon = sp.misJugadores()[masCercaBalon].distancia(sp.balon()); 
        int[] masCercanosBalon = sp.balon().indicesMasCercanos(sp.misJugadores(), masCercaBalon);
        for (int i = 0; i < masCercanosBalon.length; i++) {
        	if(sp.misJugadores()[masCercaBalon].distancia(sp.misJugadores()[masCercanosBalon[i]]) > distanciabalon){
        		comandos.add(new ComandoIrA(masCercanosBalon[i], sp.balon()));
        		break;
        	}
		}
	}

	private void presionar(SituacionPartido sp) {
		int[] cercanosPorteria = Constantes.centroArcoInf.indicesMasCercanos(sp.rivales());
		boolean[] presionando = new boolean[11];
		for(int rival: cercanosPorteria){
			for (int i = 0; i < 11; i++) {
	    		if(defiende(i) && !presionando[i]){
	    			comandos.add(new ComandoIrA(i, presionar(rival, sp)));
	    			presionando[i] = true;
	    			break;
	    		}
	    	}
		}
	}

	private Posicion presionar(int rival, SituacionPartido sp) {
		return sp.rivales()[rival].moverAngulo(sp.rivales()[rival].angulo(sp.balon()), distanciaPresion);
	}

	private void portero(SituacionPartido sp) {
		Posicion posicionPortero = new Posicion(Math.min( Math.max(sp.balon().getX(), Constantes.posteIzqArcoInf.getX()),Constantes.posteDerArcoInf.getX()), Constantes.centroArcoInf.getY() + 1);
		comandos.add(new ComandoIrA(posPortero, posicionPortero));
        comandos.add(new ComandoGolpearBalon(posPortero, Constantes.penalSup, 1, true));
	}

	private Posicion desmarque(int jugador, SituacionPartido sp) {
		Posicion posicion = sp.misJugadores()[jugador];
		double mejor = 0;
		int mejorPos = -1;
		for (int i = 0; i < posiciones.size(); i++) {
			double x  = posiciones.get(i).distancia(sp.misJugadores()[posiciones.get(i).indiceMasCercano(sp.misJugadores(), jugador)]);
			double d = marca.get(i) * x /Math.max(posicion.distancia(posiciones.get(i)), distanciaMinima)/Math.max(posiciones.get(i).distancia(alineacionDesmarque[jugador]), distanciaMinima);
			if(d > mejor){
				mejor = d;
				mejorPos = i;
			}
		}
		return generar(posiciones.get(mejorPos));
	}

	private Posicion generar(Posicion posicion) {
		return posicion;
	}

	private void calcularMarca(SituacionPartido sp) {
		for (int i = 0; i < posiciones.size(); i++) {
			marca.set(i, sp.rivales()[posiciones.get(i).indiceMasCercano(sp.rivales())].distancia(posiciones.get(i)));
		}
	}    
}
